<?php
/**************************************************************************
 *
 *   Copyright 2010 American Public Media Group
 *
 *   This file is part of AIR2.
 *
 *   AIR2 is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   AIR2 is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with AIR2.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************/

/**
 * Source
 *
 * A "Contact" in AIR2
 *
 * @property integer $src_id
 * @property string $src_uuid
 * @property string $src_username
 * @property string $src_password
 * @property string $src_first_name
 * @property string $src_last_name
 * @property string $src_middle_initial
 * @property string $src_pre_name
 * @property string $src_post_name
 * @property string $src_status
 * @property boolean $src_delete_flag
 * @property string $src_channel
 * @property integer $src_cre_user
 * @property integer $src_upd_user
 * @property timestamp $src_cre_dtim
 * @property timestamp $src_upd_dtim
 * @property Doctrine_Collection $SrcActivity
 * @property Doctrine_Collection $SrcAlias
 * @property Doctrine_Collection $SrcAnnotation
 * @property Doctrine_Collection $SrcEmail
 * @property Doctrine_Collection $SrcMailAddress
 * @property Doctrine_Collection $SrcPhoneNumber
 * @property Doctrine_Collection $SrcUri
 * @property Doctrine_Collection $SrcFact
 * @property Doctrine_Collection $SrcMediaAsset
 * @property Doctrine_Collection $SrcOrg
 * @property Doctrine_Collection $SrcPrefOrg
 * @property Doctrine_Collection $SrcPreference
 * @property Doctrine_Collection $SrcRelationship
 * @property Doctrine_Collection $SrcRelationship_2
 * @property Doctrine_Collection $SrcResponse
 * @property Doctrine_Collection $SrcResponseSet
 * @property Doctrine_Collection $SrcVita
 * @property Doctrine_Collection $SrcInquiry
 * @author rcavis
 * @package default
 */
class Source extends AIR2_Record {
    /* code_master values */
    public static $STATUS_ENGAGED = 'A';
    public static $STATUS_DEACTIVATED = 'D';
    public static $STATUS_ENROLLED = 'E';
    public static $STATUS_OPTED_OUT = 'F';
    public static $STATUS_MYSTERY = 'D';
    public static $STATUS_INVITEE = 'I';
    public static $STATUS_UNSUBSCRIBED = 'U';
    public static $STATUS_EDITORIAL_DEACTV = 'X';
    public static $STATUS_DECEASED = 'P';
    public static $STATUS_TEMP_HOLD = 'T';
    public static $CHANNEL_REFERRED = 'R';
    public static $CHANNEL_QUERY = 'Q';
    public static $CHANNEL_INTERACTIVE = 'I';
    public static $CHANNEL_EVENT = 'E';
    public static $CHANNEL_ONLINE = 'O';
    public static $CHANNEL_IDEA_GENERATOR = 'G';
    public static $CHANNEL_UNKNOWN = 'U';
    public static $CHANNEL_NEWSROOM = 'N';
    public static $CHANNEL_OUTREACH = 'S';


    /**
     * Set the table columns
     */
    public function setTableDefinition() {
        $this->setTableName('source');
        $this->hasColumn('src_id', 'integer', 4, array(
                'primary' => true,
                'autoincrement' => true,
            ));
        $this->hasColumn('src_uuid', 'string', 12, array(
                'fixed' => true,
                'notnull' => true,
                'unique' => true,
            ));
        $this->hasColumn('src_username', 'string', 255, array(
                'notnull' => true,
                'unique' => true,
            ));
        $this->hasColumn('src_password', 'string', 32, array(
                'fixed' => true,
            ));
        $this->hasColumn('src_first_name', 'string', 64, array(

            ));
        $this->hasColumn('src_last_name', 'string', 64, array(

            ));
        $this->hasColumn('src_middle_initial', 'string', 1, array(
                'fixed' => true,
            ));
        $this->hasColumn('src_pre_name', 'string', 64, array(

            ));
        $this->hasColumn('src_post_name', 'string', 64, array(

            ));
        $this->hasColumn('src_status', 'string', 1, array(
                'fixed' => true,
                'notnull' => true,
                'default' => self::$STATUS_ENGAGED,
            ));
        $this->hasColumn('src_delete_flag', 'boolean', null, array(
                'notnull' => true,
                'default' => false,
            ));
        $this->hasColumn('src_channel', 'string', 1, array(
                'fixed' => true,
            ));
        $this->hasColumn('src_cre_user', 'integer', 4, array(
                'notnull' => true,
            ));
        $this->hasColumn('src_upd_user', 'integer', 4, array(

            ));
        $this->hasColumn('src_cre_dtim', 'timestamp', null, array(
                'notnull' => true,
            ));
        $this->hasColumn('src_upd_dtim', 'timestamp', null, array(

            ));

        parent::setTableDefinition();
    }



    /**
     * Set table relations
     */
    public function setUp() {
        parent::setUp();
        $this->hasMany('SrcActivity', array(
                'local' => 'src_id',
                'foreign' => 'sact_src_id'
            ));
        $this->hasMany('SrcAlias', array(
                'local' => 'src_id',
                'foreign' => 'sa_src_id'
            ));
        $this->hasMany('SrcAnnotation', array(
                'local' => 'src_id',
                'foreign' => 'srcan_src_id'
            ));
        $this->hasMany('SrcEmail', array(
                'local' => 'src_id',
                'foreign' => 'sem_src_id'
            ));
        $this->hasMany('SrcMailAddress', array(
                'local' => 'src_id',
                'foreign' => 'smadd_src_id'
            ));
        $this->hasMany('SrcPhoneNumber', array(
                'local' => 'src_id',
                'foreign' => 'sph_src_id'
            ));
        $this->hasMany('SrcUri', array(
                'local' => 'src_id',
                'foreign' => 'suri_src_id'
            ));
        $this->hasMany('SrcFact', array(
                'local' => 'src_id',
                'foreign' => 'sf_src_id'
            ));
        $this->hasMany('SrcMediaAsset', array(
                'local' => 'src_id',
                'foreign' => 'sma_src_id'
            ));
        $this->hasMany('SrcOrg', array(
                'local' => 'src_id',
                'foreign' => 'so_src_id'
            ));
        $this->hasMany('SrcPrefOrg', array(
                'local' => 'src_id',
                'foreign' => 'spo_src_id'
            ));
        $this->hasMany('SrcPreference', array(
                'local' => 'src_id',
                'foreign' => 'sp_src_id'
            ));
        $this->hasMany('SrcRelationship', array(
                'local' => 'src_id',
                'foreign' => 'src_src_id'
            ));
        $this->hasMany('SrcRelationship as SrcRelationship_2', array(
                'local' => 'src_id',
                'foreign' => 'srel_src_id'
            ));
        $this->hasMany('SrcResponse', array(
                'local' => 'src_id',
                'foreign' => 'sr_src_id'
            ));
        $this->hasMany('SrcResponseSet', array(
                'local' => 'src_id',
                'foreign' => 'srs_src_id'
            ));
        $this->hasMany('SrcVita', array(
                'local' => 'src_id',
                'foreign' => 'sv_src_id'
            ));
        $this->hasMany('SrcInquiry', array(
                'local' => 'src_id',
                'foreign' => 'si_src_id'
            ));

        // Tagging
        $this->hasMany('TagSource as Tags', array(
                'local' => 'src_id',
                'foreign' => 'tag_xid'
            ));
    }


    /**
     * By default, discriminate throws conflicts on existing, non-matching
     * values.  Here, we'll allow overwriting the src_status.
     *
     * @param array   $data
     * @param TankSource $tsrc
     * @param int     $op
     */
    public function discriminate($data, &$tsrc, $op=null) {
        if (isset($data['src_status']) && strlen($data['src_status']) == 1) {
            $this->src_status = $data['src_status'];
        }
        parent::discriminate($data, $tsrc, $op);
    }


    /**
     *
     *
     * @param array   $orgs
     */
    public function add_orgs(array $orgs) {
        foreach ($orgs as $org) {
            $sorg = new SrcOrg();
            $sorg->so_org_id = $org->org_id;
            $this->SrcOrg[] = $sorg;
        }
    }



    /**
     * Add custom search query (from the get param 'q')
     *
     * @param AIR2_Query $q
     * @param string  $alias
     * @param string  $search
     * @param boolean $useOr
     */
    public static function add_search_str(&$q, $alias, $search, $useOr=null) {
        $a = ($alias) ? "$alias." : "";
        $str = "(".$a."src_username LIKE ? OR ".$a."src_first_name LIKE ? OR ".
            $a."src_last_name like ?)";

        if ($useOr) {
            $q->orWhere($str, array("%$search%", "$search%", "$search%"));
        }
        else {
            $q->addWhere($str, array("%$search%", "$search%", "$search%"));
        }
    }


    /**
     * Restrict user access to Sources based on which Organizations the Sources
     * have opted-in with, and what roles the User has in those Organizations.
     *
     * @param AIR2_Query $q     (reference)
     * @param User    $u     (reference)
     * @param string  $alias (optional)
     */
    public static function query_may_read(AIR2_Query &$q, User &$u, $alias=null) {
        if ($u->is_system()) {
            return;
        }
        $a = ($alias) ? "$alias." : "";
        // get org_ids with reader-or-higher roles
        $org_str = $u->get_authz_str(AIR2_AUTHZ_READER, 'xso_4.so_org_id');
        $so = "SELECT xso_4.so_src_id FROM SrcOrg xso_4 WHERE " .
            "xso_4.so_status = 'A' AND $org_str";
        $q->addWhere("${a}src_id IN ($so)");
        //Carper::carp($q->getSqlQuery());
    }



    /**
     * Restrict user access to Sources based on which Organizations the Sources
     * have opted-in with, and what roles the User has in those Organizations.
     *
     * @param AIR2_Query $q     (reference)
     * @param User    $u     (reference)
     * @param string  $alias (optional)
     */
    public static function query_may_write(AIR2_Query &$q, User &$u, $alias=null) {
        if ($u->is_system()) {
            return;
        }

        $a = ($alias) ? "$alias." : "";
        // get org_ids with writer-or-higher roles
        $org_str = $u->get_authz_str(AIR2_AUTHZ_WRITER, 'xso2.so_org_id');
        $so = "SELECT xso2.so_src_id FROM SrcOrg xso2 WHERE " .
            "xso2.so_status = 'A' AND $org_str";
        $q->addWhere("${a}src_id IN ($so)");
    }



    /**
     * Restrict user access to Sources based on which Organizations the Sources
     * have opted-in with, and what roles the User has in those Organizations.
     *
     * @param AIR2_Query $q     (reference)
     * @param User    $u     (reference)
     * @param string  $alias (optional)
     */
    public static function query_may_manage(AIR2_Query &$q, User &$u, $alias=null) {
        if ($u->is_system()) {
            return;
        }

        $a = ($alias) ? "$alias." : "";
        // get org_ids with manager-or-higher roles
        $org_str = $u->get_authz_str(AIR2_AUTHZ_MANAGER, 'xso3.so_org_id');
        $so = "SELECT xso3.so_src_id FROM SrcOrg xso3 WHERE " .
            "xso3.so_status = 'A' AND $org_str";
        $q->addWhere("${a}src_id IN ($so)");
    }





    /**
     * Returns array of related Organization org_ids that affect
     * the authorization of the Source. Only active Organizations
     * are included, and all children and parent Organizations of explicitly
     * assigned SrcOrg records are included.
     *
     * @return array $org_ids
     */
    public function get_authz() {
        $org_ids = array();
        foreach ($this->SrcOrg as $so) {
            if ($so->so_status == UserOrg::$STATUS_ACTIVE) {
                $this->_get_related_org_ids($org_ids, $so->Organization);
            }
        }
        return $org_ids;
    }


    /**
     *
     *
     * @param array   $org_ids (reference)
     * @param Organization $org
     */
    private function _get_related_org_ids(&$org_ids, $org) {
        if ($org->org_status == Organization::$STATUS_ACTIVE) {
            $org_ids[] = $org->org_id;
            foreach ($org->get_active_parents_ids() as $oid) {
                $org_ids[] = $oid;
            }
            foreach ($org->children as $o) {
                // in theory this should never happen, but if it did,
                // this would be an endless loop
                if ($o->org_id == $org->org_id) {
                    continue;
                }
                $this->_get_related_org_ids($org_ids, $o);
            }
        }
    }


    /**
     *
     *
     * @param User    $user
     * @return authz integer
     */
    public function user_may_read($user) {
        if ($user->is_system()) {
            return AIR2_AUTHZ_IS_SYSTEM;
        }

        $src_org_ids = $this->get_authz();
        $user_org_ids = array_keys($user->get_authz());
        if (count(array_intersect($src_org_ids, $user_org_ids))) {
            return AIR2_AUTHZ_IS_ORG;
        }

        return AIR2_AUTHZ_IS_DENIED;
    }


    /**
     *
     *
     * @param User    $user
     * @return authz integer
     */
    public function user_may_write($user) {
        if ($user->is_system()) {
            return AIR2_AUTHZ_IS_SYSTEM;
        }

        // abort early if cannot read
        if (!$this->user_may_read($user)) {
            return AIR2_AUTHZ_IS_DENIED;
        }

        // if the user has WRITER role in any related organization, may write
        $user_authz = $user->get_authz();
        $src_org_ids = $this->get_authz();
        foreach ($src_org_ids as $org_id) {
            if (isset($user_authz[$org_id])
                && (AIR2_AUTHZ_WRITER & $user_authz[$org_id])
            ) {
                return AIR2_AUTHZ_IS_ORG;
            }
        }

        return AIR2_AUTHZ_IS_DENIED;
    }


    /**
     *
     *
     * @param User    $user
     * @return authz integer
     */
    public function user_may_manage($user) {
        if ($user->is_system()) {
            return AIR2_AUTHZ_IS_SYSTEM;
        }

        // if the user has MANAGER role in any related organization, may write
        $user_authz = $user->get_authz();
        $src_org_ids = $this->get_authz();
        foreach ($src_org_ids as $org_id) {
            if (isset($user_authz[$org_id])
                && (AIR2_AUTHZ_MANAGER & $user_authz[$org_id])
            ) {
                return AIR2_AUTHZ_IS_ORG;
            }
        }

        return AIR2_AUTHZ_IS_DENIED;
    }


}
